﻿/**************************************************************
 * 
 * Copyright © Dmitry Kukushkin <codecity@gmail.com> 2007-2008
 * All rights reserved
 * 
 **************************************************************/

using System;
using WebMoney.SignerFX.Properties;

namespace WebMoney.SignerFX
{
    /// <summary>
    /// Представление ключей WebMoney
    /// </summary>
    public class BKeys
    {
        /// <summary>
        /// Разграничитель ключей при их конвертации в Base64
        /// </summary>
        private const char SEP = ':';

        private const int BYTES_IN_UINT = 4;

        private uint[] _expKey;
        private uint[] _modKey;

        /// <summary>
        /// Ключ E
        /// </summary>
        [CLSCompliant(false)]
        public UInt32[] ExpKey
        {
            get { return _expKey; }
            protected set { _expKey = value; }
        }

        /// <summary>
        /// Ключ N
        /// </summary>
        [CLSCompliant(false)]
        public UInt32[] ModKey
        {
            get { return _modKey; }
            protected set { _modKey = value; }
        }

        /// <summary>
        /// Создание неинициализированного объекта
        /// </summary>
        protected BKeys()
        {}

        /// <summary>
        /// Создание объекта представления ключей
        /// </summary>
        /// <param name="expKey">Ключ "e"</param>
        /// <param name="modKey">Ключ "n"</param>
        [CLSCompliant(false)]
        public BKeys(uint[] expKey, uint[] modKey)
        {
            _expKey = expKey;
            _modKey = modKey;
        }

        /// <summary>
        /// Создание объекта представления ключей
        /// </summary>
        /// <param name="value">Ключи в формате Base64, разделенные <see cref="SEP"/></param>
        public BKeys(string value)
        {
            fromString(value);
        }

        /// <summary>
        /// Строковое представление ключей
        /// </summary>
        /// <returns>Ключи в формате Base64, разделенные <see cref="SEP"/></returns>
        public override string ToString()
        {
            byte[] expKeyBytes = new byte[ExpKey.Length * BYTES_IN_UINT];
            byte[] modKeyBytes = new byte[ModKey.Length * BYTES_IN_UINT];

            Buffer.BlockCopy(ExpKey, 0, expKeyBytes, 0, expKeyBytes.Length);
            Buffer.BlockCopy(ModKey, 0, modKeyBytes, 0, expKeyBytes.Length);

            int expLen = significance(expKeyBytes);
            int modLen = significance(modKeyBytes);

            string expString = Convert.ToBase64String(expKeyBytes, 0, expLen);
            string modString = Convert.ToBase64String(modKeyBytes, 0, modLen);

            return string.Format(null,"{0}" + SEP + "{1}", expString, modString);
        }

        private void fromString(string value)
        {
            if (value.IndexOf(SEP) < 0)
                throw new ArgumentException(Resource.NoSeparator, "value");

            string[] strKeys = value.Split(SEP);

            if (strKeys.Length != 2)
                throw new ArgumentException(Resource.TwoSeparators, "value");

            try
            {
                byte[] expKeyBytes = Convert.FromBase64String(strKeys[0]);
                byte[] modKeyBytes = Convert.FromBase64String(strKeys[1]);

                ExpKey = new uint[(expKeyBytes.Length + BYTES_IN_UINT - 1) / BYTES_IN_UINT];
                ModKey = new uint[(modKeyBytes.Length + BYTES_IN_UINT - 1) / BYTES_IN_UINT];

                Buffer.BlockCopy(expKeyBytes, 0, ExpKey, 0, expKeyBytes.Length);
                Buffer.BlockCopy(modKeyBytes, 0, ModKey, 0, modKeyBytes.Length);
            }
            catch (SystemException)
            {
                throw new ArgumentException(Resource.InvalidValue, "value");
            }
        }

        // Количество значащих элементов в массиве byte
        private static int significance(byte[] value)
        {
            for (int pos = value.Length - 1; pos >= 0; pos--)
                if (0 != value[pos])
                    return pos + 1; // длина больше позиции на единицу

            return 0;
        }
    }
}